ParaSL specification draft.

Layer 1.

1.0. Intro

ParaSL is a statically typed programming language. It has several layers. Every
layer is a language in itself. Layer one is a simple language, akin to C but
with extensive type deduction.

1.1. Keywords and program structure

Following keywords are prohibited for any usage in layer one:

layer, input, output, repeat, glue, bind, if, else, for, in, return, char, int,
float, double

Every program consists of TUs, each of TUs have some layer attached.
TU is set of statements that must be executed in specified order.

layer(N, tu-name) {
  N-th layer TU goes here
}

If there is single source file of layer 0, layer keyword may be omitted.

// square.psl
layer(0, "square") {
  v0 : int = input(0);
  output(0, v0);
}

// square.0.psl
v0 : int = input(0);
output(0, v0);

You can also see comment syntax above.

1.2. Types

Every entity introduced by its first mention. Entity have associated type that
is either specified or deduced.
Special input and output are generic IO entities.
You may imagine input(0) as a typed stdin and output(0) as a typed stdout.
Definite meaning of those are given at layer 3.

v0 = 0;
v1 : double = v0;
v2 = input(0) : int; // not input(v0)
v3 : int;
output(0, v1);

For int type you may directly specify size.

v0 : int(16) = 0; // 16-bit int

Default int is 32.

Integer are two's complement signed numbers with two's complement signed wrap.
No UB here.

char type is something like std::byte in C++.

1.3. Arrays

Array entity is syntactic glue for the couple of entities of the same type.
Array locates on stack. If size is CT-known, it is fixed. If size is not
CT-known, it is something like VLA. Vector is SIMD vector and can have only
primitive element types. Vectors have both CT-known size and CT-known indices.

repeat is an expression and syntactic sugar for getting an array of equal
values.

arr0 = repeat(v0, 5); // array of 5
arr1 : int[5] = {1, 2, 3, 4}; // array of 5
arr2 : vector<int, 5> = {1, 2, 3, 4, 5}; // vector
v3 = 3;
v4 = arr0[v3];
v5 = arr2[3]; // not arr2[v3]
arr_inputs = input(0..3) : int[4]; // array of 4

1.4. Structures

Structure entity is a syntactic glue for the couple of entities of different
types. You can glue objects using the glue expression that takes expressions as
glued values. You can access members using CT-known index. Or you can define
alternative names for members right in a glue expression.

s0 = glue(v0, v2);
v3 = s0[0];
s1 = glue(s0 : x, v0 : y, arr0 : z);
v5 = s1.y; // ok v5 type same
v6 = s1.z[0]
s2 : { x : int[5], y : double } = glue(repeat(v0, 5), v1);
s2.x = repeat(0, 5);

1.5. Functions

Function entity is an abstraction mechanism. It is a callable. input expression
inside function returns function argument. If type for any input argument isn't
set clearly then this function is generic.

f0 = { v3 = input(0) + 1; v3 * v3; };
v7 = f0(2); // but not f0(2, 3)
f1 : (x) = { v3 = x + 1; v3 * v3; }; // x is generic
f2 : (x, y) = { v3 = x + y; v3 * v3; }; // x and y are generic
f3 : (x : int, y : double) : int = { v3 = x + y; v3 * v3; };
v8 = f3(v0, v1);

Normal function return is an entity generated by the last expression.

1.6. Methods

You can bind the function entity to the structure and create a method. First
argument for bind is function name, others are optional and are structure
fields that will be set as function arguments. If number of binded structure
members is less than number of required function arguments, more arguments
are given as a method arguments.

s3 : { x : int, g : () } = glue(v0, bind(f1, s3.x)); // bind requires name
v9 = s3.g(); // (v0 + 1) * (v0 + 1);
s4 : { x : int, g : () } = glue(v0, bind(f2, s4.x));
v9 = s4.g(v1); // same as call f2(s4.x, v1)

1.7. Conditionals

Nothing surprising here.

if (v2 == 0)
  v0 = f1(v0);

if (v2 == 0) {
  v0 = f1(v0);
}

if (v0 == v2) {
  v0 = f1(v0);
}
else {
  v2 = f1(v2);
}

1.8 Function early return and abbreviations

Early return is done with the return statement.

f4 : (x) = { if (x < 2) return 1; x * f4(x - 1); };

You may omit braces if the function is a single return expression.

f5 : (x) = x * x;
f6 : (x) = { x * x; };
f7 : (x) = { return x * x; };

You cannot assign functions with generic types.

f5 = f4; // fail, types are generic

f8 : (x : int) =  x + 1;
f9 : (x : int) =  x * x + 1;

But you can assign functions with concrete types.

f8 = f9; // ok, types are concrete

1.9 Loops.

Nothing surprising here. In layer one you cannot iterate over vector.

v0 = 5;
for (x in 0:5) // or x:int or 0:5:1, etc...
  v0 = v0 + x;

for (x in arr0) { // not in vector
  v0 = v0 + x;
}

Also you have traditional while loop, iterate while condition greater than zero.

Example:

n = 0;
a = 0;
b = 1;
x : int = input(0);

while(n < x) {
  n = n + 1;

  if (n == 1)
    output(0, a);

  if (n == 2)
    output(0, b);

  if (n > 2) {
    tmp = b;
    b = a + b;
    a = tmp;
    output(0, b);
  }
}

